//
//  File.swift
//
//
//  Created by didi on 2022/9/13.
//

import Fluent
import MolecularObjects
import Vapor

public protocol MetadataRepresentable: MolecularDatabaseModel {
    var systemMetadata: Metadata { get }
}

public extension MetadataRepresentable {
    func filter(_ content: String, _ req: Request) async throws -> String {
        let allFilters: [MolecularFilter] = req.invokeAllFlat(.filters)
        let filters = allFilters.filter { f in
            molecularMetadata.filters.isEmpty || molecularMetadata.filters.contains(f.key)
        }.sorted { $0.priority > $1.priority }
        var result = content
        try await filters.forEachAsync { filter in
            result = try await filter.filter(result, req)
        }

        return result
    }
}

public extension MetadataRepresentable {
    /// return already joined metadata details object
    var molecularMetadata: Metadata {
        try! joined(SystemMetadataModel.self).molecularMetadata
    }

    /// query metadata relation and load the metadata details
    static func findMetadata(for id: UUID, on db: Database) async throws -> Metadata? {
        guard let metadata = try await findMetadata(by: id, on: db) else {
            return nil
        }
        return metadata.molecularMetadata
    }

    static func queryJoinPublicMetadata(on db: Database) -> QueryBuilder<Self> {
        query(on: db).joinPublicMetadata()
    }

    static func queryJoinVisibleMetadata(on db: Database) -> QueryBuilder<Self> {
        query(on: db).joinVisibleMetadata()
    }

    static func queryJoinPublicMetadataFilterBy(path: String, on db: Database) -> QueryBuilder<Self> {
        queryJoinPublicMetadata(on: db).filterMetadata(by: path)
    }

    static func queryJoinVisibleMetadataFilterBy(path: String, on db: Database) -> QueryBuilder<Self> {
        queryJoinVisibleMetadata(on: db).filterMetadata(by: path)
    }

    func patchMetadata(_ req: Request, _ block: @escaping () -> Metadata.Patch) async throws {
        guard let metadata = try await Self.findMetadata(by: uuid, on: req.db) else {
            throw Abort(.notFound)
        }
//        try await SystemMetadataApiController().patchInput(req, metadata, block())
        try await metadata.update(on: req.db)
    }

    func publishMetadata(_ req: Request) async throws {
        try await patchMetadata(req) {
            .init(status: .published)
        }
    }

    func publishMetadataAsHome(_ req: Request) async throws {
        try await patchMetadata(req) {
            .init(slug: "", status: .published)
        }
    }
}
